package ca.unbc.cpsc672;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

import ca.unbc.cpsc672.fuzzy.IOUtils;
import ca.unbc.cpsc672.fuzzy.RuleSet;
import ca.unbc.cpsc672.fuzzy.function.MembershipFunction;
import ca.unbc.cpsc672.fuzzy.rule.IfFuzzyAnd;
import ca.unbc.cpsc672.fuzzy.rule.IfSingleSet;
import ca.unbc.cpsc672.fuzzy.rule.IfThenRule;

public class FuzzyRuleTab {
	private final class EditSupport extends EditingSupport {
		private int blockId;

		private EditSupport(int columnIndex) {
			super(tableViewer);
			blockId = columnIndex / 4;
		}

		@Override
		protected boolean canEdit(Object element) {
			RowData data = (RowData) element;
			if (data.isMultiple) {
				return true;
			} else {
				return blockId != 2;
			}
		}

		@Override
		protected CellEditor getCellEditor(Object element) {
			RowData data = (RowData) element;
			MembershipFunction mf = data.mf[blockId];
			String[] items = getLinguisticList(mf);
			ComboBoxCellEditor cbce = new ComboBoxCellEditor(
					tableViewer.getTable(), items);
			return cbce;
		}

		private String[] getLinguisticList(MembershipFunction mf) {
			return mf.getFuzzySet().getAllLinguisticVariables()
					.toArray(new String[0]);
		}

		@Override
		protected Object getValue(Object element) {
			RowData data = (RowData) element;
			MembershipFunction mf = data.mf[blockId];
			String[] items = getLinguisticList(mf);
			for (int i = 0; i < items.length; i++) {
				if (items[i].equals(mf.getLinguisticVariable()))
					return i;
			}
			return 0;
		}

		@Override
		protected void setValue(Object element, Object value) {
			RowData data = (RowData) element;
			if (blockId == data.mf.length - 1) {
				MembershipFunction mf = data.mf[blockId];
				data.rule.update(findReplaced(mf, value));
			} else {
				if (data.isMultiple) {
					MembershipFunction[] mf = new MembershipFunction[2];
					mf[0] = data.mf[0];
					mf[1] = data.mf[1];
					mf[blockId] = findReplaced(mf[blockId], value);
					data.rule.update(new IfFuzzyAnd(mf[0], mf[1]));
				} else {
					MembershipFunction mf = data.mf[0];
					data.rule.update(new IfSingleSet(findReplaced(mf, value)));
				}
			}
			tableViewer.refresh();
		}

		private MembershipFunction findReplaced(MembershipFunction mf,
				Object value) {
			String[] items = getLinguisticList(mf);
			return mf.getFuzzySet().getMembershipFunction(
					items[(Integer) value]);
		}
	}

	private final class LabelProvider extends ColumnLabelProvider {
		private int columnIndex;

		LabelProvider(int columnIndex) {
			this.columnIndex = columnIndex;
		}

		@Override
		public String getText(Object element) {
			RowData data = (RowData) element;
			int blockId = columnIndex / 4;
			int posId = columnIndex % 4;

			if (!data.isMultiple && blockId > 1)
				return "";
			if (posId == 2)
				return "IS";
			else if (posId == 0) {
				if (columnIndex == 0)
					return "IF";
				if (blockId == data.mf.length - 1)
					return "THEN";
				else
					return "AND";
			}
			MembershipFunction mf = data.mf[blockId];
			if (posId == 1)
				return mf.getFuzzySet().getName();
			else
				return mf.getLinguisticVariable();
		}
	}

	private final class ContentProvider implements IStructuredContentProvider {
		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			// TODO Auto-generated method stub

		}

		@Override
		public void dispose() {
			// TODO Auto-generated method stub

		}

		@Override
		public Object[] getElements(Object inputElement) {
			@SuppressWarnings("unchecked")
			Collection<IfThenRule> rules = (Collection<IfThenRule>) inputElement;
			ArrayList<RowData> ret = new ArrayList<>(rules.size());
			for (IfThenRule r : rules)
				ret.add(new RowData(r));
			return ret.toArray();
		}
	}

	private final class RowData {
		public final IfThenRule rule;
		public final boolean isMultiple;
		public final MembershipFunction[] mf;

		public RowData(IfThenRule rule) {
			this.rule = rule;
			MembershipFunction[] t = rule.getIfClause()
					.getMembershipFunctions();
			isMultiple = t.length == 2;
			if (isMultiple) {
				mf = new MembershipFunction[] { t[0], t[1],
						rule.getOutputMembershipFunction() };
			} else
				mf = new MembershipFunction[] { t[0],
						rule.getOutputMembershipFunction() };
		}
	}

	private RuleSet ruleSet;
	private TableViewer tableViewer;
	private Button rmBtn;

	public FuzzyRuleTab(RuleSet ruleSet) {
		this.ruleSet = ruleSet;
	}

	public Control createContents(Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		composite.setLayout(new GridLayout());
		createButtons(composite);

		tableViewer = new TableViewer(composite, SWT.H_SCROLL | SWT.V_SCROLL
				| SWT.BORDER | SWT.FULL_SELECTION);

		createColumns();
		tableViewer.setContentProvider(new ContentProvider());
		tableViewer.setInput(ruleSet.getRules());

		tableViewer
				.addSelectionChangedListener(new ISelectionChangedListener() {

					@Override
					public void selectionChanged(SelectionChangedEvent event) {
						rmBtn.setEnabled(ruleSet.getRules().size() > 1);
					}
				});
		return composite;
	}

	public void createButtons(Composite composite) {
		Composite composite1 = new Composite(composite, SWT.NONE);
		composite1.setLayout(new GridLayout(2, true));
		Button btn = new Button(composite1, SWT.NONE);
		btn.setText("Add New Rule");
		btn.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				IfThenRule rule = ruleSet.getRules().iterator().next();
				String csvString = rule.toCSVString();
				String[] str = ("tmp," + csvString).split(",");
				try {
					IfThenRule rule2 = IOUtils.loadOneRule(
							IOUtils.loadFuzzySet(), str);
					ruleSet.addRule(rule2);
					tableViewer.setInput(ruleSet.getRules());
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// TODO Auto-generated method stub

			}
		});

		rmBtn = new Button(composite1, SWT.NONE);
		rmBtn.setText("Remove Rule");
		rmBtn.addSelectionListener(new SelectionListener() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				IStructuredSelection s = (IStructuredSelection) tableViewer
						.getSelection();
				RowData d = (RowData) s.getFirstElement();
				if (d == null)
					return;
				ruleSet.removeRule(d.rule);
				tableViewer.setInput(ruleSet.getRules());
				rmBtn.setEnabled(false);
			}

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				// TODO Auto-generated method stub

			}
		});
		rmBtn.setEnabled(false);
	}

	private void createColumns() {
		for (int i = 0; i < 12; i++) {
			TableViewerColumn column = new TableViewerColumn(tableViewer,
					SWT.NONE);
			int w;
			if (i == 0 || i == 2 || i == 6)
				w = 30;
			else if (i == 4 || i == 8)
				w = 50;
			else
				w = 70;
			column.getColumn().setWidth(w);
			if (i % 4 == 3)
				column.setEditingSupport(new EditSupport(i));
			column.setLabelProvider(new LabelProvider(i));
		}
	}
}